#region Using Statements
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;

#endregion

namespace WindowsGame
{
  /// <summary>
  /// This is the main type for your game
  /// </summary>
  public class Game1 : Microsoft.Xna.Framework.Game
  {
    GraphicsDeviceManager graphics;
    ContentManager content;
    SpriteBatch batch;
    SpriteFont font;
    
    Scrolling scrolling;

    AudioEngine audioEngine;
    
    Texture2D shotTextureRight;
    Texture2D shotTextureLeft;

    Texture2D background;

    TileMap map;

    CharacterObject mCharacter;

    ShotManager mPlayerShotManager;

    EnemyObject mEnemy;

    bool GamePaused;

    int elapsedTime;

    float mVolume;

    private FPS_Counter fpsCounter;

    public Game1()
    {
      graphics = new GraphicsDeviceManager(this);
      content = new ContentManager(Services);
    }

    /// <summary>
    /// Allows the game to perform any initialization it needs to before starting to run.
    /// This is where it can query for any required services and load any non-graphic
    /// related content.  Calling base.Initialize will enumerate through any components
    /// and initialize them as well.
    /// </summary>
    protected override void Initialize()
    {
      // TODO: Add your initialization logic here
      this.Window.Title = "Test";

      font = content.Load<SpriteFont>(@"Arial");

      Global.ScreenResolution = new Vector2(1024,768);

      scrolling = new Scrolling();
      scrolling.init(content);

      audioEngine = new AudioEngine();
      audioEngine.Initialize();
      audioEngine.LoadSound(@"c:\music.mod");

      GamePaused = false;
    
      graphics.PreferredBackBufferWidth = (int)Global.ScreenResolution.X;
      graphics.PreferredBackBufferHeight = (int)Global.ScreenResolution.Y;
      graphics.ApplyChanges();

      map = new TileMap();
      map.Init(content);

      mCharacter = new CharacterObject(map);
      mCharacter.Bounds = new TileRect(50,50,32,42);
      Global.Offset = new Vector2(Global.ScreenResolution.X / 2 - mCharacter.Bounds.Width /2 - mCharacter.Bounds.X,
                                  Global.ScreenResolution.Y / 2 - mCharacter.Bounds.Height /2 - mCharacter.Bounds.Y);

      mPlayerShotManager = new ShotManager();

      mEnemy = new EnemyObject(map);
      mEnemy.Bounds = new TileRect(120,0,32,42);

      fpsCounter = new FPS_Counter(this);

      base.Initialize();
    }


    /// <summary>
    /// Load your graphics content.  If loadAllContent is true, you should
    /// load content from both ResourceManagementMode pools.  Otherwise, just
    /// load ResourceManagementMode.Manual content.
    /// </summary>
    /// <param name="loadAllContent">Which type of content to load.</param>
    protected override void LoadGraphicsContent(bool loadAllContent)
    {
      if (loadAllContent)
      {
        // character
        AnimatedSprite characterMovement = new AnimatedSprite();
        characterMovement.LoadGraphic(content.Load<Texture2D>(@"gfx\Character\character walking left"), 1, 14, 32, 42, 15);
        this.mCharacter.AnimationSprites.Add(CharacterObject.CharacterState.WalkingLeft, characterMovement);
        
        characterMovement = new AnimatedSprite();
        characterMovement.LoadGraphic(content.Load<Texture2D>(@"gfx\Character\character walking right"), 1, 14, 32, 42, 15);
        this.mCharacter.AnimationSprites.Add(CharacterObject.CharacterState.WalkingRight, characterMovement);

        characterMovement = new AnimatedSprite();
        characterMovement.LoadGraphic(content.Load<Texture2D>(@"gfx\Character\character left"), 1, 1, 32, 42, 15);
        this.mCharacter.AnimationSprites.Add(CharacterObject.CharacterState.NormalLeft, characterMovement);

        characterMovement = new AnimatedSprite();
        characterMovement.LoadGraphic(content.Load<Texture2D>(@"gfx\Character\character right"), 1, 1, 32, 42, 15);
        this.mCharacter.AnimationSprites.Add(CharacterObject.CharacterState.NormalRight, characterMovement);

        characterMovement = new AnimatedSprite();
        characterMovement.LoadGraphic(content.Load<Texture2D>(@"gfx\Character\character crouch left"), 1, 1, 32, 42, 15);
        this.mCharacter.AnimationSprites.Add(CharacterObject.CharacterState.CrouchedLeft, characterMovement);

        characterMovement = new AnimatedSprite();
        characterMovement.LoadGraphic(content.Load<Texture2D>(@"gfx\Character\character crouch right"), 1, 1, 32, 42, 15);
        this.mCharacter.AnimationSprites.Add(CharacterObject.CharacterState.CrouchedRight, characterMovement);

        characterMovement = new AnimatedSprite();
        characterMovement.LoadGraphic(content.Load<Texture2D>(@"gfx\Character\character jump or falling left"), 1, 1, 32, 42, 15);
        this.mCharacter.AnimationSprites.Add(CharacterObject.CharacterState.JumpLeft, characterMovement);

        characterMovement = new AnimatedSprite();
        characterMovement.LoadGraphic(content.Load<Texture2D>(@"gfx\Character\character jump or falling right"), 1, 1, 32, 42, 15);
        this.mCharacter.AnimationSprites.Add(CharacterObject.CharacterState.JumpRight, characterMovement);

        characterMovement = new AnimatedSprite();
        characterMovement.LoadGraphic(content.Load<Texture2D>(@"gfx\Character\character jump or falling left"), 1, 1, 32, 42, 15);
        this.mCharacter.AnimationSprites.Add(CharacterObject.CharacterState.FallingLeft, characterMovement);

        characterMovement = new AnimatedSprite();
        characterMovement.LoadGraphic(content.Load<Texture2D>(@"gfx\Character\character jump or falling right"), 1, 1, 32, 42, 15);
        this.mCharacter.AnimationSprites.Add(CharacterObject.CharacterState.FallingRight, characterMovement);

        // Enemy
        AnimatedSprite enemyMovement = new AnimatedSprite();
        enemyMovement.LoadGraphic(content.Load<Texture2D>(@"gfx\Character\character walking left"), 1, 14, 32, 42, 15);
        this.mEnemy.AnimationSprites.Add(EnemyObject.EnemyState.WalkingLeft, enemyMovement);
        
        enemyMovement = new AnimatedSprite();
        enemyMovement.LoadGraphic(content.Load<Texture2D>(@"gfx\Character\character walking right"), 1, 14, 32, 42, 15);
        this.mEnemy.AnimationSprites.Add(EnemyObject.EnemyState.WalkingRight, enemyMovement);

        enemyMovement = new AnimatedSprite();
        enemyMovement.LoadGraphic(content.Load<Texture2D>(@"gfx\Character\character left"), 1, 1, 32, 42, 15);
        this.mEnemy.AnimationSprites.Add(EnemyObject.EnemyState.NormalLeft, enemyMovement);

        enemyMovement = new AnimatedSprite();
        enemyMovement.LoadGraphic(content.Load<Texture2D>(@"gfx\Character\character right"), 1, 1, 32, 42, 15);
        this.mEnemy.AnimationSprites.Add(EnemyObject.EnemyState.NormalRight, enemyMovement);

        enemyMovement = new AnimatedSprite();
        enemyMovement.LoadGraphic(content.Load<Texture2D>(@"gfx\Character\character crouch left"), 1, 1, 32, 42, 15);
        this.mEnemy.AnimationSprites.Add(EnemyObject.EnemyState.CrouchedLeft, enemyMovement);

        enemyMovement = new AnimatedSprite();
        enemyMovement.LoadGraphic(content.Load<Texture2D>(@"gfx\Character\character crouch right"), 1, 1, 32, 42, 15);
        this.mEnemy.AnimationSprites.Add(EnemyObject.EnemyState.CrouchedRight, enemyMovement);

        enemyMovement = new AnimatedSprite();
        enemyMovement.LoadGraphic(content.Load<Texture2D>(@"gfx\Character\character jump or falling left"), 1, 1, 32, 42, 15);
        this.mEnemy.AnimationSprites.Add(EnemyObject.EnemyState.JumpLeft, enemyMovement);

        enemyMovement = new AnimatedSprite();
        enemyMovement.LoadGraphic(content.Load<Texture2D>(@"gfx\Character\character jump or falling right"), 1, 1, 32, 42, 15);
        this.mEnemy.AnimationSprites.Add(EnemyObject.EnemyState.JumpRight, enemyMovement);

        enemyMovement = new AnimatedSprite();
        enemyMovement.LoadGraphic(content.Load<Texture2D>(@"gfx\Character\character jump or falling left"), 1, 1, 32, 42, 15);
        this.mEnemy.AnimationSprites.Add(EnemyObject.EnemyState.JumpUpLookingLeft, enemyMovement);

        enemyMovement = new AnimatedSprite();
        enemyMovement.LoadGraphic(content.Load<Texture2D>(@"gfx\Character\character jump or falling right"), 1, 1, 32, 42, 15);
        this.mEnemy.AnimationSprites.Add(EnemyObject.EnemyState.JumpUpLookingRight, enemyMovement);

        enemyMovement = new AnimatedSprite();
        enemyMovement.LoadGraphic(content.Load<Texture2D>(@"gfx\Character\character jump or falling left"), 1, 1, 32, 42, 15);
        this.mEnemy.AnimationSprites.Add(EnemyObject.EnemyState.FallingLeft, enemyMovement);

        enemyMovement = new AnimatedSprite();
        enemyMovement.LoadGraphic(content.Load<Texture2D>(@"gfx\Character\character jump or falling left"), 1, 1, 32, 42, 15);
        this.mEnemy.AnimationSprites.Add(EnemyObject.EnemyState.FallingLookingLeft, enemyMovement);

        enemyMovement = new AnimatedSprite();
        enemyMovement.LoadGraphic(content.Load<Texture2D>(@"gfx\Character\character jump or falling right"), 1, 1, 32, 42, 15);
        this.mEnemy.AnimationSprites.Add(EnemyObject.EnemyState.FallingRight, enemyMovement);

        enemyMovement = new AnimatedSprite();
        enemyMovement.LoadGraphic(content.Load<Texture2D>(@"gfx\Character\character jump or falling right"), 1, 1, 32, 42, 15);
        this.mEnemy.AnimationSprites.Add(EnemyObject.EnemyState.FallingLookingRight, enemyMovement);

        //background
        background = content.Load<Texture2D>(@"gfx\Background\purple_gradient");

        shotTextureRight = content.Load<Texture2D>(@"gfx\Weapon\Shot Right");
        shotTextureLeft = content.Load<Texture2D>(@"gfx\Weapon\Shot Left");

        batch = new SpriteBatch(graphics.GraphicsDevice);

        map.Load("");
      }
    }


    /// <summary>
    /// Unload your graphics content.  If unloadAllContent is true, you should
    /// unload content from both ResourceManagementMode pools.  Otherwise, just
    /// unload ResourceManagementMode.Manual content.  Manual content will get
    /// Disposed by the GraphicsDevice during a Reset.
    /// </summary>
    /// <param name="unloadAllContent">Which type of content to unload.</param>
    protected override void UnloadGraphicsContent(bool unloadAllContent)
    {
      if (unloadAllContent)
      {
        // TODO: Unload any ResourceManagementMode.Automatic content
        content.Unload();
      }

      // TODO: Unload any ResourceManagementMode.Manual content
    }

    /// <summary>
    /// Sets the new main character state depending on several alowed key combinations.
    /// </summary>
    /// <param name="keyboardState">Current key combinations.</param>
    /// <remarks>
    /// 
    /// Bit combinations:
    /// 
    /// 0000 1111
    ///      ||||
    ///      |||+-- Key Left
    ///      ||+--- Key Right
    ///      |+---- Key Up
    ///      +----- Key Down
    /// 
    /// Note:
    /// Folowing combinations aren't alowed in this state machine:
    /// 
    /// 1. 0011 (Left AND Right)
    /// 2. 1100 (Up AND Down)
    /// 
    /// </remarks>
    private void setCharacterStateByKey(KeyboardState keyboardState)
    {
      int newKeyState = 0;

      if (keyboardState.IsKeyDown(Keys.Left) == true)
      {
        newKeyState |= (int)CharacterObject.MainKeyStates.Left;
      }

      if (keyboardState.IsKeyDown(Keys.Right) == true)
      {
        newKeyState |= (int)CharacterObject.MainKeyStates.Right;
      }

      if ((newKeyState & 0x03) == 0x03)
      {
        newKeyState = 0;
      }

      if (keyboardState.IsKeyDown(Keys.Up) == true)
      {
        newKeyState |= (int)CharacterObject.MainKeyStates.Up;
      }

      if (keyboardState.IsKeyDown(Keys.Down) == true)
      {
        newKeyState |= (int)CharacterObject.MainKeyStates.Down;
      }

      if ((newKeyState & 0x0C) == 0x0C)
      {
        newKeyState = 0;
      }

      this.mCharacter.KeyStates = newKeyState;

      this.mCharacter.performMovement(map);

    }

       
    /// <summary>
    /// Allows the game to run logic such as updating the world,
    /// checking for collisions, gathering input and playing audio.
    /// </summary>
    /// <param name="gameTime">Provides a snapshot of timing values.</param>
    protected override void Update(GameTime gameTime)
    {
      KeyboardState keyboardState = Keyboard.GetState();

      elapsedTime += gameTime.ElapsedRealTime.Milliseconds;
      
      if (keyboardState.IsKeyDown(Keys.Escape))
      {
        this.Exit();
      }

      if (elapsedTime > 200)
      {
        if (keyboardState.IsKeyDown(Keys.F12))
        {
          graphics.ToggleFullScreen();
          elapsedTime = 0;
        }

        if (keyboardState.IsKeyDown(Keys.F11))
        {
          if (graphics.SynchronizeWithVerticalRetrace == true)
          {
            graphics.SynchronizeWithVerticalRetrace = false;
          }
          else
          {
            graphics.SynchronizeWithVerticalRetrace = true;
          }
          elapsedTime = 0;
        }

        if (keyboardState.IsKeyDown(Keys.F10))
        {
          if (GamePaused == true)
          {
            GamePaused = false;
          }
          else
          {
            GamePaused = true;
          }
          elapsedTime = 0;
        }

        if (keyboardState.IsKeyDown(Keys.M))
        {
          audioEngine.PlayMusic(@"c:\music.mod");
        }
        
        if (keyboardState.IsKeyDown(Keys.S))
        {
          audioEngine.StopMusic();
        }

        if(keyboardState.IsKeyDown(Keys.OemPlus))
        {
          if(mVolume < 0)
          {
            mVolume = 0;
          }
          mVolume += 0.1f;
          audioEngine.SetVolume(mVolume);
        }

        if(keyboardState.IsKeyDown(Keys.OemMinus))
        {
          if(mVolume > 1)
          {
            mVolume = 1;
          }
          mVolume -= 0.1f;
          audioEngine.SetVolume(mVolume);
        }
      }

      if (GamePaused == false)
      {
        setCharacterStateByKey(keyboardState);      

        scrolling.update(mCharacter.Vector);
        
        this.mCharacter.update((float)gameTime.ElapsedGameTime.TotalSeconds);
        
        if (keyboardState.IsKeyDown(Keys.Space) && mCharacter.State != CharacterObject.CharacterState.Dead)
        {
          if (mCharacter.State == CharacterObject.CharacterState.CrouchedLeft ||
              mCharacter.State == CharacterObject.CharacterState.FallingLeft ||
              mCharacter.State == CharacterObject.CharacterState.JumpLeft ||
              mCharacter.State == CharacterObject.CharacterState.NormalLeft ||
              mCharacter.State == CharacterObject.CharacterState.WalkingLeft)
          {
            mPlayerShotManager.Add(new Vector2(mCharacter.Bounds.X - shotTextureLeft.Width + 10, mCharacter.Bounds.Y + 14), shotTextureLeft, new Vector2(-14,4), (float)gameTime.ElapsedGameTime.TotalSeconds);
            mPlayerShotManager.Add(new Vector2(mCharacter.Bounds.X - shotTextureLeft.Width + 10, mCharacter.Bounds.Y + 14), shotTextureLeft, new Vector2(-12,2), (float)gameTime.ElapsedGameTime.TotalSeconds);
            mPlayerShotManager.Add(new Vector2(mCharacter.Bounds.X - shotTextureLeft.Width + 10, mCharacter.Bounds.Y + 14), shotTextureLeft, new Vector2(-10,0), (float)gameTime.ElapsedGameTime.TotalSeconds);
            mPlayerShotManager.Add(new Vector2(mCharacter.Bounds.X - shotTextureLeft.Width + 10, mCharacter.Bounds.Y + 14), shotTextureLeft, new Vector2(-12,-2), (float)gameTime.ElapsedGameTime.TotalSeconds);
            mPlayerShotManager.Add(new Vector2(mCharacter.Bounds.X - shotTextureLeft.Width + 10, mCharacter.Bounds.Y + 14), shotTextureLeft, new Vector2(-14,-4), (float)gameTime.ElapsedGameTime.TotalSeconds);
          }
          else
          {
            mPlayerShotManager.Add(new Vector2(mCharacter.Bounds.X + mCharacter.Bounds.Width - 10, mCharacter.Bounds.Y + 14), shotTextureRight, new Vector2(14,4), (float)gameTime.ElapsedGameTime.TotalSeconds);
            mPlayerShotManager.Add(new Vector2(mCharacter.Bounds.X + mCharacter.Bounds.Width - 10, mCharacter.Bounds.Y + 14), shotTextureRight, new Vector2(12,2), (float)gameTime.ElapsedGameTime.TotalSeconds);
            mPlayerShotManager.Add(new Vector2(mCharacter.Bounds.X + mCharacter.Bounds.Width - 10, mCharacter.Bounds.Y + 14), shotTextureRight, new Vector2(10,0), (float)gameTime.ElapsedGameTime.TotalSeconds);
            mPlayerShotManager.Add(new Vector2(mCharacter.Bounds.X + mCharacter.Bounds.Width - 10, mCharacter.Bounds.Y + 14), shotTextureRight, new Vector2(12,-2), (float)gameTime.ElapsedGameTime.TotalSeconds);
            mPlayerShotManager.Add(new Vector2(mCharacter.Bounds.X + mCharacter.Bounds.Width - 10, mCharacter.Bounds.Y + 14), shotTextureRight, new Vector2(14,-4), (float)gameTime.ElapsedGameTime.TotalSeconds);
          }
        }

        mPlayerShotManager.Update(map);

        audioEngine.Update();

        //mEnemy.Movement(map);
        //this.mEnemy.update((float)gameTime.ElapsedGameTime.TotalSeconds);
      }

      base.Update(gameTime);
    }

    /// <summary>
    /// This is called when the game should draw itself.
    /// </summary>
    /// <param name="gameTime">Provides a snapshot of timing values.</param>
    protected override void Draw(GameTime gameTime)
    {
      graphics.GraphicsDevice.Clear(Color.Black);

      batch.Begin(SpriteBlendMode.AlphaBlend);

      //batch.Draw(background, new Rectangle(0,0,(int)Global.ScreenResolution.X,(int)Global.ScreenResolution.Y),Color.White);
      scrolling.draw(batch);

      map.Draw(batch);

      this.mCharacter.Draw(batch);

      mPlayerShotManager.Draw(batch);

      //this.mEnemy.Draw(batch);

      fpsCounter.Update(gameTime);

      batch.DrawString(font,"Character Vector X: " + mCharacter.Vector.X + " Y: " + mCharacter.Vector.Y, new Vector2(0, Global.ScreenResolution.Y - 20), Color.Yellow);
      batch.DrawString(font,"Character State: " + mCharacter.State, new Vector2(0, Global.ScreenResolution.Y - 40), Color.Yellow);
      batch.DrawString(font,"Character Position X: " + mCharacter.Bounds.X + " Y: " + mCharacter.Bounds.Y, new Vector2(0, Global.ScreenResolution.Y - 60), Color.Yellow);
      batch.DrawString(font, String.Format("Character KeyState: {0}", (int)mCharacter.KeyStates), new Vector2(0, Global.ScreenResolution.Y - 80), Color.Yellow);
      fpsCounter.Draw(batch, graphics.SynchronizeWithVerticalRetrace,0,(int)Global.ScreenResolution.Y - 100);
      batch.DrawString(font,"Game paused: " + GamePaused, new Vector2(0, Global.ScreenResolution.Y - 120), Color.Yellow);
      batch.DrawString(font,"Offset X: " + Global.Offset.X + " Y: " + Global.Offset.Y, new Vector2(0, Global.ScreenResolution.Y - 140), Color.Yellow);
      int temp = mPlayerShotManager.ShotCount();
      batch.DrawString(font,"Shot Count: " + temp, new Vector2(0, Global.ScreenResolution.Y - 160), Color.Yellow);

      batch.End();

      // TODO: Add your drawing code here

      base.Draw(gameTime);
    }
  }
}
